package com.sr365.changan.infrastructure.dao;

import java.io.Serializable;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.classic.Session;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

/**
 * Created by WangXingchao.
 * User: WangXingchao (wangxingchao@gmail.com)
 * Date: 2010-6-7
 * Time: 16:20:31
 * Description:Using to execute all operations to DB.
 */
@Repository
public class CommonDao {
    @Autowired
    private SessionFactory sessionFactory;

    //------------Several following methods are about loading object.

    @SuppressWarnings({"unchecked"})
    public <T> T load(Class<T> clazz, Serializable id) {
        return (T) getSession().get(clazz, id);
    }

    public <T> T load(Class<T> clazz, String propertyName, Object propertyValue) {
        return load(clazz, new String[]{propertyName}, new Object[]{propertyValue});
    }

    public <T> T load(Class<T> clazz, String[] propertyNames, Object[] propertyValues) {
        Criteria criteria = generateCriteria(clazz, propertyNames, propertyValues);
        return criteria == null ? null : (T) criteria.uniqueResult();
    }

    //------------Several following methods are about finding objects.

    @SuppressWarnings({"unchecked"})
    public <T> List<T> findAll(Class<T> clazz, String... orderProperties) {
        Criteria criteria = getSession().createCriteria(clazz);
        criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        if (orderProperties != null) {
            for (String orderProperty : orderProperties) {
                criteria.addOrder(Order.asc(orderProperty));
            }
        }
        return criteria.list();
    }

    public <T> List<T> find(Class<T> clazz, String propertyName, Object propertyValue) {
        return find(clazz, new String[]{propertyName}, new Object[]{propertyValue});
    }

    public <T> List<T> find(Class<T> clazz, String[] propertyNames, Object[] propertyValues) {
        Criteria criteria = generateCriteria(clazz, propertyNames, propertyValues);
        return criteria == null ? null : criteria.list();
    }

    public List find(String queryStr) {
        Query query = getSession().createQuery(queryStr);
        return query.list();
    }

    public List find(String queryStr, Object[] positionalParameters) {
        Query query = getSession().createQuery(queryStr);
        if (positionalParameters != null) {
            for (int i = 0; i < positionalParameters.length; i++) {
                Object positionalParameter = positionalParameters[i];
                query.setParameter(i, positionalParameter);
            }
        }
        return query.list();
    }

    public Object findUnique(String queryStr, Object[] positionPrameters) {
        List result = find(queryStr, positionPrameters);
        return result == null ? null : result.get(0);
    }

    public <T> T findUnique(Class<T> clazz, String queryStr, Object[] positionPrameters) {
        Object result = findUnique(queryStr, positionPrameters);
        return result == null ? null : (T) result;
    }

    //------------Save method.

    public void save(Object entity) {
        getSession().saveOrUpdate(entity);
    }

    //------------Delete method.

    public void delete(Object entity) {
        getSession().delete(entity);
    }

    // private method----------------------------------------

    private Session getSession() {
        return sessionFactory.getCurrentSession();
    }

    private <T> Criteria generateCriteria(Class<T> clazz, String[] propertyNames, Object[] propertyValues) {
        Criteria criteria = null;
        if (propertyNames != null && propertyValues != null && propertyNames.length == propertyValues.length) {
            criteria = getSession().createCriteria(clazz);
            criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
            for (int i = 0; i < propertyNames.length; i++) {
                Object value = propertyValues[i];
                if (value == null) {
                    criteria.add(Restrictions.isNull(propertyNames[i]));
                    continue;
                }
                criteria.add(Restrictions.eq(propertyNames[i], propertyValues[i]));
            }
        }
        return criteria;
    }
}
